#define GLOBAL_DIFFUSE_SCALE 1.0
#define GLOBAL_AMBIENT_SCALE 1.0
#define AMBIENT_MINZ_SCALE	 0.25

float4 VisionParams : PI_VisionParams;

// downscaled depth target
sampler2D sceneDepthSamplerDownsampled = sampler_state
{
 Texture = $ZTargetScaled;
 MinFilter = POINT;
 MagFilter = POINT;
 MipFilter = POINT;
 AddressU = Clamp;
 AddressV = Clamp;
};

sampler2D envMapSamplerRefr
{
  Texture = $SceneTarget;
  MinFilter = POINT;
  MagFilter = POINT;
  MipFilter = POINT;  
  AddressU = Clamp;
  AddressV = Clamp;  
  sRGBLookup = true; 
};

sampler2D fakeSpecMapSampler {  Texture = $CustomMap; };

////////////////////////////////////////////////
// structs

#include "ShadowCommon.cfi"

struct v2f
{
	float4 HPosition  : POSITION;

	float4 baseTC    : TEXCOORDN;		// tex coords, z is blend value for 2nd texture tile
#if %_RT_ANIM_BLEND
	float2 blendTC   : TEXCOORDN;		// 2nd tex coords
#endif

// Only output lighting related components when any lights affecting
#define USE_NORMAL  (%_LT_LIGHTS || %FAKE_SPECULAR || %ENVIRONMENT_MAP || %_RT_GLOBAL_ILLUMINATION)
#if USE_NORMAL
	half4 vNormal			: TEXCOORDN;	// w is bitangent.x (if lighting and material are used)
	#if USE_MATERIAL
		half4 vTangent  : TEXCOORDN;	// w is bitangent.y
	#endif
	half4 vWPos       : TEXCOORDN;	// w is bitangent.z
	#if %_LT_HASPROJ
		float4 projTC   : TEXCOORDN;
	#endif  
#endif

#define USE_SCREENPROJ (%_LT_LIGHTS && !PS3 && !XENON && !CAFE || %_RT_SOFT_PARTICLE || %_RT_PARTICLE_SHADOW || %REFRACTION || %SCREEN_SPACE_DEFORMATION || %DEFORMATION)
#if USE_SCREENPROJ
	float4 screenProj  : TEXCOORDN;
#endif

#if %_RT_FOG && !%REFRACTION
	half4 localFogColor : TEXCOORDN;
#endif

#if %_RT_MOTION_BLUR && !%REFRACTION
	half4 vMotionBlur : TEXCOORDN;
	half3 vMotionBlur2 : TEXCOORDN;
#endif

	float4 Color    : COLOR0;
};

/////////////////////////////////////////////////

void Particle_Convert_TC( float3 baseTC, inout v2f OUT )
{
	// Apply tiling params to TCs
	OUT.baseTC.xyz = baseTC * TexTileSize.xyz;
	OUT.baseTC.z += TexTileSize.w;
	
	float tileX = floor(OUT.baseTC.z) * TexTileSize.x;
#if %_RT_ANIM_BLEND
	// Add offset of tile to xy coords, set z to blend fraction between tiles
  OUT.baseTC.z = frac(OUT.baseTC.z);

	// Compute coords of 2nd blended tile
	OUT.blendTC.xy = OUT.baseTC.xy;
	OUT.blendTC.x += frac(tileX + TexTileSize.x);
	OUT.blendTC.y += floor(tileX + TexTileSize.x) * TexTileSize.y;
#endif
	OUT.baseTC.x += frac(tileX);
	OUT.baseTC.y += floor(tileX) * TexTileSize.y;
}

// vertex shader
v2f ParticleTransform(in app2vertParticleGeneral IN, in half4 vSSCenterMotion)
{
	v2f OUT = (v2f)0;

	//////////////////////////////////////////////////////
	// Setup particle
  
	vert2FragParticleGeneral outParticle = (vert2FragParticleGeneral)0;  
	float4 vPos = IN.Position;

#if %_RT_MOTION_BLUR
	outParticle.SSCenterMotion = vSSCenterMotion;
#endif
	Particle_Setup_General( vPos, g_VS_ViewProjMatr, IN, outParticle);
  
	OUT.HPosition = outParticle.Position;
#if %_RT_MOTION_BLUR && !%REFRACTION
	OUT.vMotionBlur = outParticle.MotionBlur;
	OUT.vMotionBlur2 = outParticle.MotionBlur2;
#endif

#if USE_SCREENPROJ
	// Output the screen-space texture coordinates
	OUT.screenProj = HPosToScreenTC(OUT.HPosition);
#endif

  //////////////////////////////////////////////////////

  // Apply tiling params to tcs
	Particle_Convert_TC(IN.baseTC.xyz, OUT);

#if %DEFORMATION
  // output original coordinates
	OUT.baseTC.zw = IN.baseTC.xy;
#endif

#ifdef %_RT_SOFT_PARTICLE
	half size = length(IN.XAxis + IN.YAxis);
	OUT.screenProj.z = 4.h / size;
	#if USE_MATERIAL
	  OUT.screenProj.z *= SoftParticlesScale;
	#endif
#endif
 #ifdef CAFE
	OUT.Color.xyzw = IN.Color.yxwz;
 #else
	OUT.Color = GetInputColor(IN.Color);
 #endif	
	// Apply alpha scale
	OUT.Color.w *= ParticleParams.x;

	#if USE_MATERIAL
		OUT.Color.xyz *= MatDifColor;
	#endif

#if USE_NORMAL
	OUT.vWPos.xyz = vPos.xyz;

	float3 vNormal = cross(IN.YAxis, IN.XAxis);
	float3 vBitangent;

	//////////////////////////////////////////////////////
	if (ParticleParams.w)
	{
		// camera-facing tangent space
		float3 vFlatNormal = vfViewPos - vPos;
		OUT.vNormal.xyz = vPos.xyz - IN.Position.xyz;
		// rotate tangents for fake round geometry
		OUT.vNormal.xyz = OUT.vNormal.xyz * dot(vFlatNormal, vFlatNormal) - vFlatNormal * dot(vFlatNormal, OUT.vNormal.xyz);
		OUT.vNormal.xyz = normalize(OUT.vNormal.xyz);

	#if USE_MATERIAL
			float3 vAxis = cross(vNormal, OUT.vNormal.xyz);
			float c = dot(vNormal, OUT.vNormal.xyz);
			OUT.vTangent.xyz = IN.XAxis * c + cross(vAxis, IN.XAxis);
			vBitangent = IN.YAxis * c + cross(vAxis, IN.YAxis);
	#endif
	}
	else
	{
		OUT.vNormal.xyz = normalize(vNormal);
		if (dot(OUT.vNormal.xyz, vfViewPos - vPos) < 0)
			OUT.vNormal.xyz = -OUT.vNormal.xyz;
	#if USE_MATERIAL
			OUT.vTangent.xyz = IN.XAxis;
			vBitangent = IN.YAxis;
	#endif
	}

	#if USE_MATERIAL
		// Output bitangent in w components to save 1 interpolator (2 extra alu in pixel shader though)
		OUT.vNormal.w = vBitangent.x;
		OUT.vTangent.w = vBitangent.y;
		OUT.vWPos.w = vBitangent.z;
	#endif

	#if %_LT_HASPROJ
		// 0 type = directional light source (no attenuation)
		// 1 type = omni light source
		// 2 type = projected light source
		const int aLType[4] = { %_LT_0_TYPE, %_LT_1_TYPE, %_LT_2_TYPE, %_LT_3_TYPE };  
		for (int i=0; i<%_LT_NUM; i++)
		{
      if (aLType[i] == LT_PROJECTED)
				OUT.projTC = mul(LightMatrix, vPos);
		}
	#endif
#endif // USE_NORMAL

#if %_RT_FOG && !%REFRACTION
  OUT.localFogColor = GetVolumetricFogColor( vPos );
#endif

  return OUT;
}

//////////////////////////////////////////////////////////////////////

v2f ParticleVS(app2vertParticleGeneral IN)
{
	half4 vSSCenterMotion = 0;
#if %_RT_MOTION_BLUR
	const half4 vCenterPos = mul(g_VS_ViewProjMatr, IN.Position);
	const half4 vCenterPosPrev = mul(ViewProjMatrPrev, IN.Position);
	// Current screen space position of particle center (xy), screen space camera motion vector (zw)
	vSSCenterMotion = half4(vCenterPos.xy/vCenterPos.w, vCenterPosPrev.xy/vCenterPosPrev.w);
	vSSCenterMotion.zw = vSSCenterMotion.xy - vSSCenterMotion.zw;
#endif

	// Get hardware-appropriate tex coord order.
#ifdef CAFE  
  IN.baseTC.xyzw = IN.baseTC.xywz;
#else  
  IN.baseTC = GetInputColor(IN.baseTC);
#endif  
  return ParticleTransform(IN, vSSCenterMotion);
}

//////////////////////////////////////////////////////////////////////

#ifdef D3D10
app2vertParticleGeneral ParticleVS_GSPassThrough(app2vertParticleGeneral IN)
{
	return IN;
}

v2f ParticleTransformQuad(in app2vertParticleGeneral IN, int i, half4 vSSCenterMotion)
{
	// Quad shape
	IN.baseTC.x = i&1;
	IN.baseTC.y = (i&2)/2;
  
  return ParticleTransform(IN, vSSCenterMotion);
}

v2f ParticleTransformOct(in app2vertParticleGeneral IN, int i, half4 vSSCenterMotion)
{
	// Octagonal shape
	static const half2 arrShapeOffs[] =
	{
		half2(0.294h, 0.h),    //0
		half2(0.706h, 0.h),    //1
		half2(0.h,    0.294h), //7
		half2(1.h,    0.294h), //2
		half2(0.h,    0.706h), //6
		half2(1.h,    0.706h), //3
		half2(0.294h, 1.h),    //5
		half2(0.706h, 1.h),    //4
	};
	IN.baseTC.xy = arrShapeOffs[i];
  
  return ParticleTransform(IN, vSSCenterMotion);
}

[maxvertexcount(8)]
void ParticleGS(point app2vertParticleGeneral IN[1], inout TriangleStream<v2f> OutStream)
{
	v2f OUT = (v2f) 0;

  IN[0].baseTC = GetInputColor(IN[0].baseTC);

	half4 vSSCenterMotion = 0;
#if %_RT_MOTION_BLUR
	const half4 vCenterPos = mul(g_VS_ViewProjMatr, IN[0].Position);
	const half4 vCenterPosPrev = mul(ViewProjMatrPrev, IN[0].Position);
	// Current screen space position of particle center (xy), screen space camera motion vector (zw)
	vSSCenterMotion = half4(vCenterPos.xy/vCenterPos.w, vCenterPosPrev.xy/vCenterPosPrev.w);
	vSSCenterMotion.zw = vSSCenterMotion.xy - vSSCenterMotion.zw;
#endif

	if (IN[0].baseTC.x)
	{
		// octagon
		for (int v = 0; v < 8; v++)
		{
			OUT = ParticleTransformOct(IN[0], v, vSSCenterMotion);
			OutStream.Append(OUT);
		}
	}
	else
	{
		// quad
		for (int v = 0; v < 4; v++)
		{
			OUT = ParticleTransformQuad(IN[0], v, vSSCenterMotion);
			OutStream.Append(OUT);
		}
	}
	OutStream.RestartStrip();
}

#endif // D3D10


//////////////////////////////////////////////////////////////////////

void ApplyTexCoordsDeformation( inout v2f IN )
{
#if %SCREEN_SPACE_DEFORMATION

  half4 baseColor = tex2D(diffuseSampler, IN.baseTC.xy);
  half3 vBump = half3(0,0,1);
  // apply rescale
  PerturbationScale *= IN.screenProj.w* 0.05;

  float2 tcProj = IN.screenProj.xy/IN.screenProj.w;
  float2 tcAnim = float2(0.5, AnimGenParams.z * AnimSpeed + 0.5);
	PerturbationScale *= (tcProj-0.5);

  vBump.xy = FetchNormalMap(customSampler, PerturbationScale + tcAnim);
  vBump.xy += FetchNormalMap(customSampler, PerturbationScale * 1.5 + tcAnim);
  vBump.xy = vBump.xy * 2.0  - 2.0;

  IN.baseTC.xy += vBump.xy * PerturbationStrength * baseColor.w;

#elif %DEFORMATION
  float2 tcDeform = IN.baseTC.zw;
  float4 tcAnim = AnimGenParams.z * DeformAnimSpeed * float4(0.9, 0.9, 2, 2) + float4(0.5, 0.5, 0.75, 1.0);
  float4 tcBump = (tcDeform.xyxy - 0.5)* DeformTile * float4(1,1,2,2) + tcAnim.xyzw;

  half2 vBump = FetchNormalMap(customSampler, tcBump.xy);
  vBump.xy += FetchNormalMap(customSampler, tcBump.zw);
  vBump.xy = vBump.xy * 2.0  - 2.0;

  IN.baseTC.xy += vBump.xy * DeformAmount;
#endif
}

//////////////////////////////////////////////////////////////////////

void ApplyColorLookup( in v2f IN, inout half4 cBaseColor )
{

#if %COLOR_LOOKUP
  half fLum = dot(cBaseColor.xyz, 0.333 );
  cBaseColor.xyz = saturate ( cBaseColor.w *ColLookupAmplitude * tex2D( customSecSampler,  ColLookupColPhase * fLum  ) );
#elif %COLOR_LOOKUP_ANIM
  half fLum = dot(cBaseColor.xyz, 0.333 );
  cBaseColor.xyz = saturate ( cBaseColor.w * ColLookupAmplitude * tex2D( customSecSampler, abs(frac( ColLookupPhase + ColLookupColPhase * fLum + AnimGenParams.x * ColLookupFrequency)*2-1) ) );
#endif

}

//////////////////////////////////////////////////////////////////////

half3 Saturate(const in half3 cColor, const in half fSat)
{
	const half3 cColorWgt = half3(0.3086, 0.6094, 0.0820) * (1.h - fSat);
	half4x4 matTrans = {	half4(half3(fSat, 0, 0) + cColorWgt.x, 0.h),
												half4(half3(0, fSat, 0) + cColorWgt.y, 0.h),
												half4(half3(0, 0, fSat) + cColorWgt.z, 0.h),
												half4(0.h, 0.h, 0.h, 1.h) };
	return mul(half4(cColor, 1.h), matTrans).xyz;
}

pixout ParticlePS(v2f IN)
{
	#if PS3
		#pragma sce-cgc("-texformat default COMPRESSED_RGBA_S3TC_DXT5");  
		// suggested by gpad
		// #pragma sce-cgc("-regcount 3"); 
	#endif

  pixout OUT = (pixout) 0;

  #if %_RT_DEBUG0 || %_RT_DEBUG1 || %_RT_DEBUG2 || %_RT_DEBUG3
    DebugOutput(OUT.Color, float4(IN.baseTC.xy, 0, 1));
    return OUT;
  #endif

	//OUT.Color = IN.Color;
	//return OUT;

	#if USE_MATERIAL
		ApplyTexCoordsDeformation( IN );
	#endif

	#if %_RT_MOTION_BLUR && !%REFRACTION
		half4 vUVGrad = half4(ddx(IN.baseTC.xy), ddy(IN.baseTC.xy));
		vUVGrad = max(vUVGrad, IN.vMotionBlur2.xyxy);
		half4 baseColor = tex2Dgrad(diffuseSamplerAniso, IN.baseTC.xy, vUVGrad.xy, vUVGrad.zw);
	#else
		half4 baseColor = tex2D(diffuseSampler, IN.baseTC.xy);
	#endif
	//half4 tempColor = baseColor;

	#if %_RT_ANIM_BLEND
		// Blend in second tile
		half4 blendColor = tex2D(diffuseSampler, IN.blendTC.xy);
		baseColor += (blendColor - baseColor) * IN.baseTC.z;
	#endif

	#if USE_MATERIAL
		ApplyColorLookup( IN, baseColor );;
	#endif

	baseColor *= IN.Color;
	clip(baseColor.w - PBAlphaTest.w);

  half4 refrColor = 0;

  half4 finalColor = half4(0,0,0,0);

	#if USE_NORMAL

		#if USE_MATERIAL
			half3 bumpNormal = GetNormalMap(bumpMapSampler, IN.baseTC.xy);
			#if %_RT_ANIM_BLEND
				half3 blendNormal = GetNormalMap(bumpMapSampler, IN.blendTC.xy);
				bumpNormal += (blendNormal - bumpNormal) * IN.baseTC.z;
			#endif

			#if %REFRACTION
				half2 refrTC = (IN.screenProj.xy/IN.screenProj.w) + bumpNormal.xy * RefrBumpScale.x * baseColor.a;
				refrColor = DecodeHDRBuffer( tex2D(envMapSamplerRefr, refrTC ) );
			#endif

			const float3 vBitangent = float3(IN.vNormal.w, IN.vTangent.w, IN.vWPos.w);
			half3x3 mTangentToWS = half3x3(normalize(IN.vTangent.xyz), normalize(vBitangent), normalize(IN.vNormal.xyz));
			// Put normal in world space
			half3 vNormal = mul(bumpNormal, mTangentToWS);
		#else
			half3 vNormal = normalize(IN.vNormal.xyz);
		#endif

		const float3 vWPos = IN.vWPos.xyz;

		#if %_RT_PARTICLE_SHADOW
			float4 vShadowWPos = float4(vWPos,1);
			#if VS_ALPHABLEND
				vShadowWPos.xyz += vNormal * min(baseColor.w * vAdaption.w, 7.h);
			#else
				vShadowWPos.xyz += vNormal * min(dot(baseColor.xyz, vAdaption.w), 5.h);
			#endif
			vert2fragShadowCommon shadowTC;
			GenShadowTC(vShadowWPos, shadowTC);
			half2 vNoiseTC = IN.screenProj.xy/IN.screenProj.w;
			half fShadow = saturate( ShadowDepthTest(shadowTC, vNoiseTC).r );
		#endif

		const int aLType[4] = {%_LT_0_TYPE, %_LT_1_TYPE, %_LT_2_TYPE, %_LT_3_TYPE};  
    // Only specular pass is used for light-map light sources
    #ifdef D3D10
    [unroll]
    #endif
    for (int i=0; i<%_LT_NUM; i++)
    {
      int nType = aLType[i];
      half3 filterColor = 1;
      half fFallOff = 1;
  
      float3 vLight;  
      if (nType == LT_DIRECTIONAL)
      {
        // Some optimisations for sun light (per-frame parameters and hardcoded values)  
        // also no attenuation and light normalization
        vLight = g_PS_SunLightDir.xyz;  
      }
      else
      {  
        float4 WorldLightPos = LGetPosition(i);
        float3 vLightWS = WorldLightPos.xyz - vWPos;
        vLight = normalize(vLightWS.xyz);                                                           // 3 alu
        fFallOff = GetAttenuation(vLightWS.xyz, WorldLightPos.w);                                   // 2 alu
      }

    #if %_LT_HASPROJ
      // Get projector filter color if needed
      if (aLType[i] == LT_PROJECTED)
      {
       	float4 P = IN.projTC.xyzw;
        P.xy /= P.w;
	      float4 clipFrustum;
	      clipFrustum.xy = P.xy;
	      clipFrustum.zw = 1.0-P.xy;
	      clip( clipFrustum );
        filterColor = tex2D(projMapSampler, P ); // alpha contains luminance
        //filterColor = texCUBE(projCubeSampler, IN.projTC.xyz);
      }
    #endif

      // Calculate diffuse contribution.
      half3 lVec = vLight;
      half fIllum = dot(lVec.xyz, vNormal.xyz);

      // Back lighting in 2 steps: first increase unclamped dot-product (wrapping lighting around sphere), then clamp to backlight/2
      fIllum += (1 - fIllum) * ParticleParams.z;
      fIllum = max(fIllum, ParticleParams.z * 0.5);

			#if %_RT_PARTICLE_SHADOW
				fIllum *= fShadow;
			#endif
      half3 vDif = LGetDiffuse(i).xyz * fIllum * fFallOff * filterColor; // fFallOff and filterColor gets optimized away by compiler when equal 1

      finalColor.xyz += vDif.xyz;
    }
	#endif // USE_NORMAL

	half3 cDiffAcc = finalColor.xyz;

	#if %_RT_AMBIENT && %_RT_DIFFUSE_CUBEMAP
#ifdef CAFE
		finalColor.xyz += DecodeRGBK(texCUBE( diffuseCMSampler, vNormal.xyz ), HDR_FAKE_MAXOVERBRIGHT * 6, false).rgb;
#else
		finalColor.xyz += DecodeRGBK(texCUBE( diffuseCMSampler, vNormal.xyz ), HDR_FAKE_MAXOVERBRIGHT * 6).rgb;
#endif

	#endif
	
	// Scale by diffuse lighting multiplier.
	finalColor.xyz *= Ambient.w * GLOBAL_DIFFUSE_SCALE;

	#if %_RT_AMBIENT
		// Add ambient/emissive only once when ambient requested (on additive light passes should be skipped)
		half3 ambientColor = Ambient.xyz * ((vNormal.z + 1.0) * 0.5 * (1.0 - AMBIENT_MINZ_SCALE) + AMBIENT_MINZ_SCALE) * GLOBAL_AMBIENT_SCALE * GLOBAL_DIFFUSE_SCALE;
		cDiffAcc.xyz += ambientColor;
		finalColor.xyz += ambientColor;
	#endif

	#if %_RT_GLOBAL_ILLUMINATION
		half3 cGI = LPVGetGlobalIllumination(vNormal.xyz, vWPos);
		// hack to increase color bleeding even with lower GI amount
		cGI = max(Saturate(cGI, 2), 0);
		#if USE_MATERIAL
			cGI *= GlobalIlluminationAmount;
		#else
			cGI *= g_vGIAttenuation.w;
		#endif
		finalColor.xyz += cGI;
	#endif

  // Apply material color
  finalColor.xyz *= baseColor.xyz;
	#if !VS_ALPHABLEND
		finalColor.xyz *= baseColor.w;
	#endif

	#if USE_MATERIAL

		#if %REFRACTION
			finalColor.xyz = refrColor.xyz;
		#endif

		#if %FAKE_SPECULAR || %ENVIRONMENT_MAP
		{
			//  %FAKE_SPECULAR - to be removed

			half3 cSpecularAcc = 0;
			half3 vView = normalize(PS_WorldViewPos.xyz-IN.vWPos.xyz);
			half fNdotE = ( dot(vView.xyz, vNormal.xyz));                                        // 1 alu
			half3 vReflVec = (2.0h * fNdotE * vNormal.xyz) - vView.xyz;  

			#if %ENVIRONMENT_MAP

				cSpecularAcc.xyz = GetEnvironmentCMap(envMapSamplerCUBE, vReflVec.xyz, MatSpecColor.w);    
				half fFresnel = FresnelBias + FresnelScale * pow(saturate( 1.0h - fNdotE ), FresnelPower); // 4 inst
				cSpecularAcc.xyz *= fFresnel;

			#else
				cSpecularAcc.xyz = tex2D( fakeSpecMapSampler, SpecBumpScale * vReflVec.xy);
			#endif	

			finalColor.xyz += cSpecularAcc * MatSpecColor * cDiffAcc;
		}
		#endif
	#endif


  finalColor.w = saturate(baseColor.w);


  #if %_RT_SOFT_PARTICLE
		const half sceneDepth = DecodeSceneDepth( sceneDepthSampler, IN.screenProj ).x;
		const half scaleBackground = saturate( (sceneDepth - IN.screenProj.w) * IN.screenProj.z );
		const half fadeParticle = min( scaleBackground, saturate(IN.screenProj.w ) );

    #if VS_ALPHABLEND
      finalColor.a *= fadeParticle;
    #else  
      finalColor.rgb *= fadeParticle;
      finalColor.a = 0.0;
    #endif
    ////////////////////////////////////////////////////////////////
  #endif
  
  #if %_RT_FOG && !%REFRACTION
    #if VS_ALPHABLEND && %_RT_AMBIENT
      finalColor.xyz = lerp( IN.localFogColor.xyz, finalColor.xyz, IN.localFogColor.w );
      finalColor.xyz = lerp( AvgFogVolumeContrib.xyz, finalColor.xyz, AvgFogVolumeContrib.w );
    #else
      finalColor.xyz *= IN.localFogColor.w;
      finalColor.xyz *= AvgFogVolumeContrib.w;  
    #endif
  #endif  

  // Re-scale range
  #if !%REFRACTION
    // Add glow in general passes
    half3 vGlowCol = baseColor.w * baseColor.rgb * (half3)GlowParams.xyz;
    #if %_RT_SOFT_PARTICLE
      finalColor.rgb += fadeParticle * vGlowCol;
    #else
      finalColor.rgb += vGlowCol;
    #endif

    finalColor.rgb *= PS_HDR_RANGE_ADAPT_MAX;
  #endif

	// Motion blur
	#if %_RT_MOTION_BLUR
	#if !%REFRACTION
		// NOTE: vMotionBlur.xy might be normalized and length passed separately from VS/GS,
		// then fast expand of particle (eg.explosion) would have the same motion strength over the whole particle
		const half fMotionLength = length(IN.vMotionBlur.xy);
		if (fMotionLength > 1e-4h)
		{
			const half fMotionStrength = saturate(fMotionLength * 10);
			// (0,1) for central part towards stretched parts
			const half fStretchRatio = saturate(abs(dot(IN.vMotionBlur.zw, IN.vMotionBlur.xy / fMotionLength)) * 1.3h);
			half fMBOpacityCoef = fMotionStrength * fStretchRatio;
			// (0,1) for center towards edges
			//half fEdgeScale = saturate(dot(IN.vMotionBlur.zw, IN.vMotionBlur.zw));// + 0.2h * fMotionStrength);
			//fEdgeScale *= fEdgeScale;
			fMBOpacityCoef *= fMBOpacityCoef;
			//fMBOpacityCoef *= fMBOpacityCoef;
			fMBOpacityCoef = saturate(1.h - fMBOpacityCoef);
			//fMBOpacityCoef *= 1.h - fEdgeScale * fMotionStrength * saturate(2.h * (1.h - fStretchRatio));

			// Energy conservation for high glow/HDR particles based on rough screen space area expand
			fMBOpacityCoef *= IN.vMotionBlur2.z;

			#if VS_ALPHABLEND
				finalColor.a *= fMBOpacityCoef;
			#else
				finalColor.rgb *= fMBOpacityCoef;
				finalColor.a = 0.0;
			#endif
		}
	#endif
	#endif

//	#if %_RT_SAMPLE0 // half res particles debug view
//		finalColor.rgb = saturate(finalColor.rgb) * HALFRES_DEBUG_COLOR;
//	#endif

  //finalColor = tempColor;
  //finalColor.a = 1;
  HDROutput( OUT, finalColor, 1 );

  return OUT;
}


/////////////////////////////////
// technique

#ifdef D3D10 && !CAFE
technique Particles
<
  string Script =  
    "TechniqueShadowPass=ShadowPassParticleGS;"
>
{
  pass p0
  {  
    VertexShader = ParticleVS_GSPassThrough() ParticleVS;
    GeometryShader = ParticleGS();
    PixelShader = ParticlePS() ParticlePS;  

    ZEnable = true;
    ZWriteEnable = false;
    CullMode = None;
  }
}
#endif

technique Particles
<
  string Script =  
		"TechniqueCustomRender=CustomRenderPass;"
    "TechniqueShadowPass=ShadowPassParticle;"
>
{
  pass p0
  {

    VertexShader = ParticleVS() ParticleVS;
    PixelShader = ParticlePS() ParticlePS;  

    ZEnable = true;
    ZWriteEnable = false;
    CullMode = None;
  }
}

#include "CommonShadowPass.cfi"

///////////////////////////////////////////////////////////////////////////////////
// Shadow pass
///////////////////////////////////////////////////////////////////////////////////

vert2fragShadow Particle_ShadowTransform(app2vertParticleGeneral IN)
{
  vert2fragShadow OUT = (vert2fragShadow)0;  

  vert2FragParticleGeneral outParticle;  

  float4 vPos = IN.Position;
  Particle_Setup_General( vPos, g_VS_ViewProjMatr, IN, outParticle);

  OUT.HPosition = outParticle.Position;


#ifdef VS_ALPHABLEND
  Particle_Convert_TC(IN.baseTC.xyz, OUT);
#endif
  
  //TOFIX: replace by separate projected noise tex coords
  OUT.RandDirTC.xy = mul(vPos.xyz, (float3x3) TexGen0).xy / 800;

  // compute shadow tex coords and depth
  GenShadowTC(vPos, OUT.shadowTC);

  return OUT;
}

vert2fragShadow Particle_ShadowVS(app2vertParticleGeneral IN)
{
  return Particle_ShadowTransform(IN);
}

#ifdef D3D10

app2vertParticleGeneral Particle_ShadowVS_GSPassThrough(app2vertParticleGeneral IN)
{
  return IN;
}

vert2fragShadow Particle_ShadowTransform(in app2vertParticleGeneral IN, int i)
{
  IN.baseTC.x = i&1;
	IN.baseTC.y = (i&2)/2;
  
  return Particle_ShadowTransform(IN);
}


[maxvertexcount(4)]
void Particle_ShadowGS(point app2vertParticleGeneral In[1], inout TriangleStream<vert2fragShadow> OutStream)
{
  vert2fragShadow Out = (vert2fragShadow) 0;
  for (int v=0; v<4; v++)
  {  
    Out = Particle_ShadowTransform(In[0], v);
    OutStream.Append(Out);
  }
  OutStream.RestartStrip();
}

#endif

pixout_cl Particle_ShadowPS(vert2fragShadow IN)
{
  pixout_cl OUT;

  float4 vCompare = (float4)0;

  half baseAlpha = 1;
#if VS_ALPHABLEND
  baseAlpha = tex2D(ShadowDiffuseMapSampler, IN.baseTC.xy).w;
  baseAlpha *= ShadowAmbient.a;
#endif  

#ifndef %_RT_GSM_COMBINED
//non-combined GSM

  vCompare = ShadowDepthTest(IN.shadowTC, IN.RandDirTC.xy);

#else  
//GSM ATLAS

  vCompare = ShadowDepthTestAtlas(IN.shadowTC, IN.RandDirTC.xy);

//GSM ATLAS
#endif

  //shadow fading - is not supported currently
  //vCompare *= IN.RandDirTC.z;

  OUT.Color = 1 - vCompare;
#if VS_ALPHABLEND
  OUT.Color.a = baseAlpha;
#endif  

  OUT.Color.a = 0.1;

  return OUT;
}

#ifdef D3D10

technique ShadowPassParticleGS
<
  string Script =
        "NoLights;"
>
{
  pass p0
  {  
    VertexShader = Particle_ShadowVS_GSPassThrough() ShadowVS;
    GeometryShader = Particle_ShadowGS();
    PixelShader = Particle_ShadowPS() ShadowPS;

    AlphaBlendEnable = true;
    SrcBlend = SrcAlpha;
    DestBlend = InvSrcAlpha;

    ZEnable = true;
    ZWriteEnable = false;
    CullMode = Back;
  }
}

#endif

technique ShadowPassParticle
<
  string Script =
        "NoLights;"
>
{
  pass p0
  {
    VertexShader = Particle_ShadowVS() ShadowVS;
    PixelShader = Particle_ShadowPS() ShadowPS;

    AlphaBlendEnable = true;
    SrcBlend = SrcAlpha;
    DestBlend = InvSrcAlpha;

    ZEnable = true;
    ZWriteEnable = false;
    CullMode = Back;
  }
}

////////////////////////////////////////////////////////////////////
//                Custom render Pass (thermal/sonar vision)
////////////////////////////////////////////////////////////////////

float4 VisionMtlParams : PB_VisionMtlParams;

// Interpolator bound
struct v2fCustomRender
{
  float4 HPosition  : POSITION;

  float4 baseTC     : TEXCOORDN;
	float4 screenProj : TEXCOORDN;

  float4 Color     : COLOR0;
};

///////////////// vertex shaders //////////////////

v2fCustomRender ParticleTransformCustomRender(app2vertParticleGeneral IN)
{
  v2f ParticleOut = ParticleTransform(IN, 0);  	

  v2fCustomRender OUT = (v2fCustomRender) 0;
  OUT.HPosition = ParticleOut.HPosition;

  OUT.baseTC.xy = ParticleOut.baseTC;

  OUT.Color = ParticleOut.Color;
	OUT.Color.xyz *= OUT.Color.w;
	// Apply alpha scale
	OUT.Color.w *= ParticleParams.x;
	OUT.screenProj = HPosToScreenTC(OUT.HPosition);

  #ifdef %_RT_SOFT_PARTICLE
    OUT.screenProj.z = ParticleOut.screenProj.z;
  #endif

  return OUT;
}

v2fCustomRender ParticleCustomRenderVS(app2vertParticleGeneral IN)
{
  return ParticleTransformCustomRender(IN);
}


///////////////// pixel shaders //////////////////
pixout ParticleCustomRenderPS(v2fCustomRender IN)
{
  pixout OUT = (pixout)0;  
  half4 baseColor = tex2D(diffuseSampler, IN.baseTC.xy);

  baseColor *= IN.Color;
	clip(baseColor.w - PBAlphaTest.w);
		
	baseColor.w =  saturate(baseColor.w);
#if USE_MATERIAL    
	VisionParams.x = saturate( VisionMtlParams.x + VisionParams.x );
#endif

  OUT.Color = saturate( VisionParams.x )* baseColor;
  OUT.Color.xyz *= baseColor.w;
	OUT.Color.xyz = dot( OUT.Color.xyz, 1);
  
#if %_RT_SOFT_PARTICLE

		const half sceneDepth = DecodeSceneDepth( sceneDepthSampler, IN.screenProj ).x;
		const half scaleBackground = saturate( (sceneDepth - IN.screenProj.w) * IN.screenProj.z );
		const half fadeParticle = min( scaleBackground, saturate(IN.screenProj.w - 0.2h) );

    #if VS_ALPHABLEND
      OUT.Color.a *= fadeParticle;
    #else  
      OUT.Color.rgb *= fadeParticle;
      OUT.Color.a = 0.0;
    #endif
		////////////////////////////////////////////////////////////////

#endif

#if %_RT_SAMPLE0 // half res particles debug view
	OUT.Color.rgb = saturate(OUT.Color.rgb) * HALFRES_DEBUG_COLOR;
#endif

	return OUT;
}

#ifdef D3D10

app2vertParticleGeneral ParticleCustomRenderVS_GSPassThrough(app2vertParticleGeneral IN)
{
  return IN;
}

v2fCustomRender ParticleTransformCustomRender(in app2vertParticleGeneral IN, int i)
{
  IN.baseTC.x = i&1;
	IN.baseTC.y = (i&2)/2;
  
  return ParticleTransformCustomRender(IN);
}


[maxvertexcount(4)]
void ParticleCustomRenderGS(point app2vertParticleGeneral In[1], inout TriangleStream<v2fCustomRender> OutStream)
{
  v2fCustomRender Out = (v2fCustomRender) 0;
  for (int v=0; v<4; v++)
  {  
    Out = ParticleTransformCustomRender(In[0], v);
    OutStream.Append(Out);
  }
  OutStream.RestartStrip();
}

technique CustomRenderGS
{
  pass p0
  {
    VertexShader = ParticleVS_GSPassThrough() ParticleVS;
    GeometryShader = ParticleCustomRenderGS();
    PixelShader = ParticleCustomRenderPS() ParticlePS;

    ZEnable = true;
    ZWriteEnable = false;
    CullMode = None;
  }
}

#endif

technique CustomRenderPass
{
  pass p0
  {
    VertexShader = ParticleCustomRenderVS() ParticleVS;
    PixelShader = ParticleCustomRenderPS() ParticlePS;

    ZEnable = true;
    ZWriteEnable = false;
    CullMode = None;

    SrcBlend = ONE;
    DestBlend = ONE;
    AlphaBlendEnable = true;

		IgnoreMaterialState = true;
  }
}
